/**************************************************************************/
/*! 
    @file     tsl2550.c
    @author   K. Townsend (microBuilder.eu)
	
    @brief    Drivers for the TAOS TSL2550 I2C digital luminosity sensor

    @section DESCRIPTION

    The TSL2550 is a 16-bit digital luminosity sensor the approximates
    the human eye's response to light.  It contains one broadband
    photodiode that measures visible plus infrared light (channel 0)
    and one infrared photodiode (channel 1).

    @section EXAMPLE

    @code
    #include "drivers/sensors/tsl2550/tsl2550.h"
    ...
    uint16_t broadband, ir;
    uint32_t lux;

    // Initialise luminosity sensor
    tsl2550Init();

    // Optional ... default setting is 400ms with no gain
    // Set timing to 101ms with no gain
    tsl2550SetTiming(TSL2550_INTEGRATIONTIME_101MS, TSL2550_GAIN_0X);

    // Check luminosity level and calculate lux
    tsl2550GetLuminosity(&broadband, &ir);
    lux = tsl2550CalculateLux(broadband, ir);
    printf("Broadband: %u, IR: %u, Lux: %d %s", broadband, ir, lux, CFG_PRINTF_NEWLINE);

    @endcode

    @section LICENSE

    Software License Agreement (BSD License)

    Copyright (c) 2010, microBuilder SARL
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holders nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
#include "tsl2550.h"
#include "../core/systick.h"

extern volatile uint8_t   I2CMasterBuffer[I2C_BUFSIZE];
extern volatile uint8_t   I2CSlaveBuffer[I2C_BUFSIZE];
extern volatile uint32_t  I2CReadLength, I2CWriteLength;

static bool _tsl2550Initialised = false;

/**************************************************************************/
/*! 
    @brief  Sends a single command byte over I2C
*/
/**************************************************************************/
tsl2550Error_t tsl2550WriteCmd (uint8_t cmd)
{
  // Clear write buffers
  uint32_t i;
  for ( i = 0; i < I2C_BUFSIZE; i++ )
  {
    I2CMasterBuffer[i] = 0x00;
  }

  I2CWriteLength = 2;
  I2CReadLength = 0;
  I2CMasterBuffer[0] = TSL2550_ADDRESS;       // I2C device address
  I2CMasterBuffer[1] = cmd;                   // Command register
  i2cEngine();
  return TSL2550_ERROR_OK;
}
tsl2550Error_t tsl2550Read8(uint8_t reg, uint8_t *value)
{
  // Clear write buffers
  uint32_t i;

  for ( i = 0; i < I2C_BUFSIZE; i++ )
  {
    I2CMasterBuffer[i] = 0x00;
  }

  I2CWriteLength = 1;
  I2CReadLength = 1;
  I2CMasterBuffer[0] = TSL2550_ADDRESS | TSL2550_READBIT;
  i2cEngine();
//
//  // Shift values to create properly formed integer (low byte first)
  *value = I2CSlaveBuffer[0] ;
 // *value = (I2CSlaveBuffer[0] | (I2CSlaveBuffer[1] << 8));
  //

  return TSL2550_ERROR_OK;
}

/**************************************************************************/
/*! 
    @brief  Initialises the I2C block
*/
/**************************************************************************/
tsl2550Error_t tsl2550Init(void)
{
  // Initialise I2C
  if (i2cInit(I2CMASTER) == false)
  {
    return TSL2550_ERROR_I2CINIT;    /* Fatal error */
  }
  
  _tsl2550Initialised = true;

  // Set default integration time and gain
 // tsl2550SetTiming(_tsl2550IntegrationTime, _tsl2550Gain);
  tsl2550WriteCmd(TSL2550_CONTROL_POWERON );

  // Note: by default, the device is in power down mode on bootup

  return TSL2550_ERROR_OK;
}


/**************************************************************************/
/*! 
    @brief  Reads the luminosity on both channels from the TSL2550
*/
/**************************************************************************/
tsl2550Error_t tsl2550GetLuminosity (uint8_t *broadband, uint8_t *ir)
{
  if (!_tsl2550Initialised) tsl2550Init();

  tsl2550Error_t error = TSL2550_ERROR_OK;

  //test if chip works, poweron again.
  error = tsl2550WriteCmd(TSL2550_CONTROL_POWERON);

  // Reads two byte value from channel 0 (visible + infrared)
  // set ADC0
  error = tsl2550WriteCmd(TSL2550_RD_ADC0);
  if (error) return error;
  error = tsl2550Read8(TSL2550_RD_ADC0, broadband);
  if (error | !(*broadband & 0x80)) return error; // check for error or no valid flag
  *broadband &= 0x7f; // remove valid flag

  // Reads two byte value from channel 1 (infrared)
  // set ADC1
  error = tsl2550WriteCmd(TSL2550_RD_ADC1);
  if (error) return error;
  error = tsl2550Read8(TSL2550_RD_ADC1, ir);
  if (error | !(*ir & 0x80)) return error; // check for error or no valid flag
  *ir &= 0x7f; // remove valid flag


  return error;
}


 /*
  * This function is described into Taos TSL2550 Designer's Notebook
  * pages 2, 3.
  */
 uint32_t tsl2550CalculateLux(uint8_t ch0, uint8_t ch1)
 {
         unsigned int lux;

         /* Look up count from channel values */
         uint16_t c0 = count_lut[ch0];
         uint16_t c1 = count_lut[ch1];
         /*
          * Calculate ratio.
          * Note: the "128" is a scaling factor
          */
         uint8_t r = 128;

         /* Avoid division by 0 and count 1 cannot be greater than count 0 */
         if (c1 <= c0)
                 if (c0) {
                         r = c1 * 128 / c0;

                         /* Calculate LUX */
                         lux = ((c0 - c1) * ratio_lut[r]) / 256;
                 } else
                         lux = 0;
         else // c1 > c0 --> finster!
                 return 0;

         /* LUX range check */
         return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
 }

uint32_t tsl2550GetLux()
{
	 uint8_t broadband, ir;
	 uint8_t lux;
	 tsl2550GetLuminosity(&broadband, &ir);
	 lux = tsl2550CalculateLux(broadband,ir);
	 return lux;

}
